﻿
using Xacml.Core;
using Xacml.Core.Policy;
using System.Collections;
using System.Collections.Specialized;

namespace Xacml.Core.Runtime
{
  public class Policy : IMatchEvaluable, IObligationsContainer
  {
    private StringCollection _allResources = new StringCollection();
    private RuleCollection _rules = new RuleCollection();
    private ObligationCollection _obligations = new ObligationCollection();
    private IDictionary _variables = (IDictionary) null;
    private Decision _evaluationValue;
    private PolicyElement _policy;
    private Target _target;

    public StringCollection AllResources
    {
      get
      {
        return this._allResources;
      }
    }

    public ObligationCollection Obligations
    {
      get
      {
        return this._obligations;
      }
    }

    public IDictionary VariableDefinition
    {
      get
      {
        return this._variables;
      }
    }

    public Policy(EvaluationEngine engine, PolicyElement policy)
    {
      this._policy = policy;
      if (policy.Target != null)
      {
        this._target = new Target((TargetElement) policy.Target);
        foreach (TargetItemBaseReadWrite itemBaseReadWrite in (CollectionBase) policy.Target.Resources.ItemsList)
        {
          foreach (ResourceMatchElement resourceMatchElement in (CollectionBase) itemBaseReadWrite.Match)
          {
            if (!this._allResources.Contains(resourceMatchElement.AttributeValue.Contents))
              this._allResources.Add(resourceMatchElement.AttributeValue.Contents);
          }
        }
      }
      foreach (RuleElement rule1 in (CollectionBase) policy.Rules)
      {
        Rule rule2 = new Rule(rule1);
        this._rules.Add(rule2);
        foreach (string str in rule2.AllResources)
        {
          if (!this._allResources.Contains(str))
            this._allResources.Add(str);
        }
      }
    }

    public TargetEvaluationValue Match(EvaluationContext context)
    {
      TargetEvaluationValue targetEvaluationValue = TargetEvaluationValue.Indeterminate;
      context.Trace("Evaluating Target...");
      context.AddIndent();
      try
      {
        targetEvaluationValue = TargetEvaluationValue.Match;
        if (this._target != null)
          targetEvaluationValue = this._target.Evaluate(context);
        return targetEvaluationValue;
      }
      finally
      {
        context.TraceContextValues();
        context.RemoveIndent();
        context.Trace("Target: {0}", (object) targetEvaluationValue);
      }
    }

    public Decision Evaluate(EvaluationContext context)
    {
      context.Trace("Evaluating policy: {0}", (object) this._policy.Description);
      context.AddIndent();
      context.CurrentPolicy = this;
      try
      {
        if (this._policy.SchemaVersion == XacmlVersion.Version20 && this._variables == null)
        {
          context.Trace("Evaluating variables...");
          this._variables = (IDictionary) new Hashtable();
          foreach (VariableDefinitionElement variableDefinition1 in (IEnumerable) this._policy.VariableDefinitions.Values)
          {
            Xacml.Core.Runtime.VariableDefinition variableDefinition2 = new Xacml.Core.Runtime.VariableDefinition(variableDefinition1);
            this._variables.Add((object) variableDefinition1.Id, (object) variableDefinition2);
          }
        }
        switch (this.Match(context))
        {
          case TargetEvaluationValue.Match:
            context.Trace("Rule combination algorithm: {0}", (object) this._policy.RuleCombiningAlgorithm);
            this._evaluationValue = context.Engine.CreateRuleCombiningAlgorithm(this._policy.RuleCombiningAlgorithm).Evaluate(context, this._rules);
            break;
          case TargetEvaluationValue.NoMatch:
            this._evaluationValue = Decision.NotApplicable;
            break;
          case TargetEvaluationValue.Indeterminate:
            this._evaluationValue = Decision.Indeterminate;
            break;
        }
        context.Trace("Policy: {0}", (object) this._evaluationValue);
        this._obligations = new ObligationCollection();
        if (this._evaluationValue != Decision.Indeterminate && this._evaluationValue != Decision.NotApplicable && (this._policy.Obligations != null && this._policy.Obligations.Count != 0))
        {
          foreach (ObligationElement obligationElement in (CollectionBase) this._policy.Obligations)
          {
            if (obligationElement.FulfillOn == Effect.Deny && this._evaluationValue == Decision.Deny || obligationElement.FulfillOn == Effect.Permit && this._evaluationValue == Decision.Permit)
            {
              context.Trace("Adding obligation: {0} ", (object) obligationElement.ObligationId);
              this._obligations.Add((ObligationElementReadWrite) obligationElement);
            }
          }
        }
        return this._evaluationValue;
      }
      finally
      {
        context.RemoveIndent();
        context.CurrentPolicy = (Policy) null;
      }
    }
  }
}
